#include "utilities.h"

//Re-initialize the variables as before
void init()
{
	minNeighCount = 0;
	minNeighFlag = false;
	notifyFlag = false;
	joinFlag = false;
	globalAcceptSock = -1;
	globalStatusTime = -1;
	checkResponseTime = -1;
	joinCount = 0;
	port_recieve = 0;
	port_timetolive = 0;
	checkFlag = false;
	
	isBeacon = false;
	resetFlag = false;
	
	nodeIndex = 0;
	dieChild = false;
	serverShutdown = false;
	notifyCount = 0;
	myFromUOIDList.clear();
	myUOIDList.clear();
	myMap.clear();
	myTimers.clear();
	statusResp.clear();
	initMap.clear();
	joinRequestMap.clear();
	statusResponseList.clear();
	allNeighbors.clear();
	eventQueue.clear();
	writeMap.clear();
	neighborMap.clear();
}

//Do the same functionality as if starting the node again.
int restartMain(int argc, char*argv[])
{
	int i = 0;
	char obj[512];
	multimap<int, int>::iterator itStatusResp;
	multimap<int, int>::iterator it1;
	multimap<int, int>::iterator it2;
	set<int>::iterator itSet;
	memset(statusFile, '\0', 128);
	
	//parse the ini file and store the information in initMap map.
	parser_function(argc,argv);
	FILE * pFile;
	char filepath[512];
	strcpy(filepath,initMap["HomeDir"].asStr);
	strcat(filepath,"/");
	strcat(filepath,initMap["LogFilename"].asStr);
	pFile = fopen(filepath,"a");
	fclose(pFile);
	//start the alarm for the autoshutdown
	alarm((int)(initMap["AutoShutdown"].asDouble));
	
	//store the initial variables
	port_recieve = (unsigned int)(initMap["Port"].asDouble);
	port_timetolive = (unsigned int)(initMap["TTL"].asDouble);
	if(printFlag==1)
	{
		cout<<port_recieve<<endl;
	}
	memset(port_recieve_str, '\0', 512);
	sprintf(port_recieve_str,"%d",port_recieve);
	memset(hostname, '\0', 512);
	gethostname(hostname, sizeof(hostname));
	
	memset(node_id, '\0', 512);
	memset(node_instance_id, '\0', 512);
	
	strcpy(node_id,hostname);
	strcat(node_id,"_");
	strcat(node_id,port_recieve_str);
	
	time_t seconds;
	seconds = time(NULL);
	sprintf(node_instance_id, "%s_%ld", node_id, seconds);
	
	//If a non beacon node is set then form the ini neighbors list first then join accordingly.
	if(!isBeacon)
	{
		bool joinPrFlag = false;
		//See if the reset flag is set, if set then remove the files in home directory and proceed as a new joining
		//node.
		if(resetFlag)
		{
			if(printFlag==1)
			{
				cout<<"Reset is given"<<endl;
			}
			joinPrFlag = true;
			char path[128];
			memset(path, '\0', 128);
			strcpy(path, initMap["HomeDir"].asStr);
			strcat(path, "/init_neighbor_list");
			remove(path);
			memset(path, '\0', 128);
			strcpy(path, initMap["HomeDir"].asStr);
			strcat(path, "/servert.log");
			remove(path);
		}
		else
		{
			//If reset is not given then check if the init_neighbor_list file exists or not then proceed
			//accordingly.
			if(printFlag==1)
			{
				cout<<"Reset not given"<<endl;
			}
			struct stat fileStat;
			char initFile[128];
			memset(initFile, '\0', 128);
			strcpy(initFile, initMap["HomeDir"].asStr);
			strcat(initFile, "/init_neighbor_list");
			if(stat(initFile, &fileStat) < 0)
			{
				if(printFlag==1)
				{
					cout<<"Files problem"<<endl;
				}
				joinPrFlag = true;
			}
			else
			{
				if(printFlag==1)
				{
					cout<<"Going to parse"<<endl;
				}
				parserInitNeighFile();
			}
		}
		if(joinPrFlag)
		{
			if(printFlag==1)
			{
				cout<<"non beacon node"<<endl;
			}
			struct addrinfo hints, *servinfo, *p;
			int rv;
			int sockfd;
			int addrflag = 0;
			char port[10];
			char host[128];
			int logPort;
			memset(port, '\0', 10);
			memset(host, '\0', 128);
			
			memset(&hints, 0, sizeof hints);
			hints.ai_family = AF_INET;
			hints.ai_socktype = SOCK_STREAM;
			for (itAllNeighbors=allNeighbors.begin(); itAllNeighbors!=allNeighbors.end(); itAllNeighbors++)
			{
				memset(port, '\0', 10);
				sprintf(port, "%d", itAllNeighbors->port);
				logPort = itAllNeighbors->port;
				strcpy(host,itAllNeighbors->hostName);
				if ((rv = getaddrinfo(host, port, &hints, &servinfo)) != 0) 
				{
					continue;
				}
				addrflag = 1;
				for(p = servinfo; p != NULL; p = p->ai_next) 
				{
					if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) 
					{
						perror("client: socket");
						continue;
					}

					if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) 
					{
						close(sockfd);
						if(printFlag==1)
						{
							perror("client: connect");
						}
						continue;
					}

					break;
				}

				if (p == NULL) 
				{
					if(printFlag==1)
					{
						fprintf(stderr, "client: failed to connect\n");
					}
					continue;
				}
				else
				break;
			}
			if(addrflag==0)
			{
				if(printFlag==1)
				{
					printf("The beacon network is not yet setup");
				}
				exit(0);
			}
			// loop through all the results and connect to the first we can
		
			freeaddrinfo(servinfo); // all done with this structure
			
			//1. Create the join message structure
			join_msg joinReq;
			joinReq.hostlocation = initMap["Location"].asDouble;
			joinReq.hostport = initMap["Port"].asDouble;
			memset(joinReq.hostname, '\0', 512);
			strcpy(joinReq.hostname, hostname);
			int jnrqDataLen = 0;
			jnrqDataLen = sizeof(uint32_t)+sizeof(uint16_t)+strlen(joinReq.hostname);
			
			//2. Create the Message header structure
			char objM[128];
			strcpy(objM, "JNRQ");
			
			msg_header joinReqHdr;
			createMsgHdrStr(&(joinReqHdr),0xFC,initMap["TTL"].asDouble,obj,node_instance_id, jnrqDataLen);
			
			char joinBuff[512];
			memset(joinBuff, '\0', 512);
			//Create the message buffer
			fillMsgHdrBuffer(joinReqHdr, joinBuff);
			memcpy(&(joinBuff[27]),&(joinReq.hostlocation),sizeof(uint32_t));
			memcpy(&(joinBuff[31]),&(joinReq.hostport),sizeof(uint16_t));
			memcpy(&(joinBuff[33]),&(joinReq.hostname),strlen(joinReq.hostname));
			listBuffStruct lbtemp;
			lbtemp.logtype = 1;
			lbtemp.portNumber = logPort;
			memcpy(lbtemp.listBuff, joinBuff,sizeof joinBuff);
			lbtemp.datalength = 27 + jnrqDataLen;
			//Now the write the request onto the beacon socket
			if(write(sockfd, joinBuff, (27+jnrqDataLen)) == -1)
			{
				close(sockfd);
				exit(1);
			}
			struct timeval logTime;
			gettimeofday(&logTime, NULL);
			write_to_log(lbtemp, 1, logTime);
			join_msg tempJoinMsg;
			memcpy(&(tempJoinMsg.hostport), &(joinBuff[31]), sizeof(uint16_t));
			if(printFlag==1)
			{
				cout<<"Got Join Request msg from - "<<tempJoinMsg.hostport<<endl;
			}
			struct timeval joinTimeout;
			joinTimeout.tv_sec = initMap["JoinTimeout"].asDouble;
			joinTimeout.tv_usec = 0;
			fd_set fds;
			FD_ZERO(&fds);
			FD_SET(sockfd, &fds);
			while(1)
			{
				char headBuff[27];
				int numbytes;
				int timeoutFlag = -1;
				
				timeoutFlag = select(sockfd+1, &fds, NULL, NULL, &joinTimeout);
				struct timeval t1, t2;
				double ti1, ti2;
				gettimeofday(&t1,NULL);
				ti1 = t1.tv_sec + (t1.tv_usec/(1e+6));
				if(timeoutFlag == 0)
				{
					close(sockfd);
					//Get the neighbors for this node.
					int noOfNeigh = initMap["InitNeighbors"].asDouble;
					int minNoOfNeigh = 0;
					//clear the previous neighbor list used for join request
					allNeighbors.clear();
					for(itNeighborMap=neighborMap.begin(); itNeighborMap!=neighborMap.end(); itNeighborMap++)
					{
						if(noOfNeigh == 0)
						{
							break;
						}
						noOfNeigh--;
						minNoOfNeigh++;
						beacon neigh;
						neigh.port = (*itNeighborMap).second.port;
						strcpy(neigh.hostName, (*itNeighborMap).second.hostname);
						allNeighbors.push_back(neigh);
					}
					if(minNoOfNeigh < (int)initMap["InitNeighbors"].asDouble)
					{
						if(printFlag==1)
						{
						cout<<"The condition for minimum number of neighbors is not satisfied"<<endl;
						}
						exit(1);
					}
					//Create the init-neighbors-list.
					char neighborFilepath[512];
					strcpy(neighborFilepath,initMap["HomeDir"].asStr);
					strcat(neighborFilepath,"/init_neighbor_list");
					FILE * pFile;
					pFile = fopen(neighborFilepath,"a");
					if(pFile == NULL)
					{
						if(printFlag==1)
						{
							cout<<"init_neighbor_list file is not present"<<endl;
						}
						exit(1);
					}
					char neighbor[512];
					for (itAllNeighbors=allNeighbors.begin(); itAllNeighbors!=allNeighbors.end(); itAllNeighbors++)
					{
						memset(neighbor, '\0', 512);
						sprintf(neighbor, "%s:%d\n", itAllNeighbors->hostName, itAllNeighbors->port);
						fputs(neighbor, pFile);
					}
					fclose(pFile);
					break;
				}
				if((numbytes = read(sockfd, headBuff, 27)) == 0)
				{
					if(printFlag==1)
					{
						cout<<"Error while reading"<<endl;
					}
					close(sockfd);
					exit(1);
				}
				if(numbytes == -1)
				{
					if(printFlag==1)
					{
						cout<<"Owned socket is closed"<<endl;
					}
					close(sockfd);
					exit(1);
				}
				if(dieChild)
				{
					close(sockfd);
					exit(1);
				}
				
				msg_header joinRespHdr;
				fillMsgHdrStructure(joinRespHdr, headBuff);
				
				char dataBuff[joinRespHdr.datalength];
				if((numbytes = read(sockfd, dataBuff, joinRespHdr.datalength)) == 0)
				{
					if(printFlag==1)
					{
						cout<<"Error while reading"<<endl;
					}
					close(sockfd);
					exit(1);
				}
				if(numbytes == -1)
				{
					if(printFlag==1)
					{
						cout<<"Owned socket is closed"<<endl;
					}
					close(sockfd);
					exit(1);
				}
				if(dieChild)
				{
					close(sockfd);
					exit(1);
				}
				listBuffStruct lbtemp;
				lbtemp.logtype = 0;
				lbtemp.portNumber = logPort;
				memcpy(lbtemp.listBuff, headBuff,sizeof headBuff);
				memcpy(&(lbtemp.listBuff[27]), dataBuff,joinRespHdr.datalength);
				lbtemp.datalength = 27 + joinRespHdr.datalength;
				gettimeofday(&logTime, NULL);
				write_to_log(lbtemp, 0, logTime);
				
				join_response_msg joinRespMsg;
				memcpy(&(joinRespMsg.uoid), &(dataBuff[0]), 20);
				memcpy(&(joinRespMsg.distance), &(dataBuff[20]), sizeof(uint32_t));
				memcpy(&(joinRespMsg.hostport), &(dataBuff[24]), sizeof(uint16_t));
				memset(joinRespMsg.hostname, '\0', 512);
				memcpy(&(joinRespMsg.hostname), &(dataBuff[26]), joinRespHdr.datalength-20-sizeof(uint32_t)-sizeof(uint16_t));
				neighborStruct neighbor;
				memcpy(&(neighbor.port), &(joinRespMsg.hostport), sizeof(uint16_t));
				memset(neighbor.hostname, '\0', 512);
				memcpy(&(neighbor.hostname), &(joinRespMsg.hostname), strlen(joinRespMsg.hostname));
				//Push the response on the list
				neighborMap.insert(pair<int, neighborStruct>(joinRespMsg.distance,neighbor));
				if(printFlag==1)
				{
					cout<<neighbor.hostname<<endl;
					cout<<neighbor.port<<endl;
				}
				gettimeofday(&t2,NULL);
				ti2 = t2.tv_sec + (t2.tv_usec/(1e+6));
				joinTimeout.tv_sec = joinTimeout.tv_sec - (ti2 - ti1);
			}
		}
	}
	
	myHello.hostport = port_recieve;
	strcpy(myHello.hostname,hostname);
	strcpy(obj,"HLLO");

	createMsgHdrStr(&(myHello.header),0xFA,1,obj,node_instance_id,sizeof(myHello.hostport)+strlen(myHello.hostname));

	beaconmap temp1;
	temp1.socknumber = -1;
	temp1.connectflag = 0;
	
	pthread_create(&acceptThread,NULL,accept_f,(void*)1);
	
	pthread_t connectThread[allNeighbors.size()];
	
	for (itAllNeighbors=allNeighbors.begin(); itAllNeighbors!=allNeighbors.end(); itAllNeighbors++)
	{
		if((unsigned int)(itAllNeighbors->port)!=port_recieve)
		{
			pthread_create(&connectThread[i++],NULL,connect_f,(void*)itAllNeighbors->port);
			myMap[itAllNeighbors->port] = temp1;
			if(printFlag==1)
			{
			cout<<itAllNeighbors->port<<"------>"<<itAllNeighbors->hostName<<endl;
			}
		}
	}

	pthread_create(&eventDisp, NULL, eventDispatcher, NULL);
	pthread_create(&keepAliveThread, NULL, keepAlive, NULL);
	pthread_create(&timerThr, NULL, timerSetThread, NULL);
	pthread_create(&commandLine, NULL, terminalCommands, NULL);
	i = 0;
	for (itAllNeighbors=allNeighbors.begin(); itAllNeighbors!=allNeighbors.end(); itAllNeighbors++)
	{
		if((unsigned int)(itAllNeighbors->port)!=port_recieve)
		{
			pthread_join(connectThread[i++],NULL);
		}
	}
	
	//If not beacon and does not satisfy the min neighbors value then do a soft restart.
	if(!isBeacon && minNeighFlag)
	{
		if((double)minNeighCount < initMap["MinNeighbors"].asDouble)
		{
			checkFlag = true;
			if(printFlag==1)
			{
				cout<<"Min neigh not satisfied"<<endl;
			}
			pthread_cancel(commandLine);
			alarm(1);
		}
	}
	
	usleep(10);
	pthread_join(acceptThread, NULL);
	pthread_join(commandLine, NULL);
	if(printFlag==1)
	{
		cout<<"Print status"<<endl;
		cout<<globalStatusTime<<" llllllllll "<<statusFile<<" llll "<<statusResp.size()<<endl;
		cout<<"Here it is"<<endl;
	}
	if(globalStatusTime != -1 && statusFile[0] != '\0')
	{
		globalStatusTime = -1;
		FILE *file;
		char path[256];
		memset(path, '\0', 256);
		strcpy(path, statusFile);
		file = fopen(path, "w");
		if(file == NULL)
		{
			if(printFlag==1)
			{
				cout<<"Problem in opening the file"<<endl;
			}
			exit(0);
		}
		char dataStatus[256];
		memset(dataStatus, '\0', 256);
		strcpy(dataStatus, "V -t * -v 1.0a5\n");
		fputs(dataStatus, file);
		memset(dataStatus, '\0', 256);
		sprintf(dataStatus, "n -t * -s %d -c red -i black\n", port_recieve);
		fputs(dataStatus, file);
		set<int> setNodes;
		pthread_mutex_lock(&statusRespLock);
		for(itStatusResp=statusResp.begin(); itStatusResp!=statusResp.end(); itStatusResp++)
		{
			setNodes.insert((*itStatusResp).first);
			setNodes.insert((*itStatusResp).second);
		}
		pthread_mutex_unlock(&statusRespLock);
		pthread_mutex_lock(&statusRespLock);
		for(itSet=setNodes.begin(); itSet!=setNodes.end(); itSet++)
		{
			if((int)port_recieve != *itSet)
			{
				memset(dataStatus, '\0', 256);
				sprintf(dataStatus, "n -t * -s %d -c red -i black\n", *itSet);
				fputs(dataStatus, file);
			}
		}
		pthread_mutex_unlock(&statusRespLock);
		multimap<int, int>::iterator it;
		multimap<int, int> statusRespClone;
		pthread_mutex_lock(&statusRespLock);
		for(it1=statusResp.begin(); it1!=statusResp.end(); it1++)
		{
			for(it2=it1; it2!=statusResp.end(); it2++)
			{
				
				if(((*it1).first == (*it2).second) && ((*it1).second == (*it2).first))
				{
					statusResp.erase(it2);
				}						
			}
		}
		pthread_mutex_unlock(&statusRespLock);
		pthread_mutex_lock(&statusRespLock);
		for(itStatusResp=statusResp.begin(); itStatusResp!=statusResp.end(); itStatusResp++)
		{
				memset(dataStatus, '\0', 256);
				sprintf(dataStatus, "l -t * -s %d -d %d -c blue\n", (*itStatusResp).first, (*itStatusResp).second);
				fputs(dataStatus, file);
		}
		pthread_mutex_unlock(&statusRespLock);
		memset(statusFile, '\0', 128);
		fclose(file);
	}
	if(checkFlag == false)
	{
		if(printFlag==1)
		{
			cout<<"End of node"<<endl;
		}
	}
	else
	{
		cout<<"Soft Restart"<<endl;
		char path[128];
		memset(path, '\0', 128);
		strcpy(path, initMap["HomeDir"].asStr);
		strcat(path, "/init_neighbor_list");
		remove(path);
		init();
		restartMain(argc,argv);
	}
	return 0;
}
